import React from "react";
import * as ReactDOM from "react-dom";
import KeyCode from "rc-util/lib/KeyCode";
import contains from "rc-util/lib/Dom/contains";
import Animate from "rc-animate";
import LazyRenderBox from "./LazyRenderBox";
import { RadiusBottomrightOutlined } from "@ant-design/icons";
import "./index.css";
let uuid = 0;
const MULTIPLE = 1;

/* eslint react/no-is-mounted:0 */

function getScroll(w, top) {
  let ret = w[`page${top ? "Y" : "X"}Offset`];
  const method = `scroll${top ? "Top" : "Left"}`;
  if (typeof ret !== "number") {
    const d = w.document;
    ret = d.documentElement[method];
    if (typeof ret !== "number") {
      ret = d.body[method];
    }
  }
  return ret;
}

function setTransformOrigin(node, value) {
  const style = node.style;
  ["Webkit", "Moz", "Ms", "ms"].forEach(prefix => {
    style[`${prefix}TransformOrigin`] = value;
  });
  style[`transformOrigin`] = value;
}

function offset(el) {
  const rect = el.getBoundingClientRect();
  const pos = {
    left: rect.left,
    top: rect.top
  };
  const doc = el.ownerDocument;
  const w = doc.defaultView || doc.parentWindow;
  pos.left += getScroll(w);
  pos.top += getScroll(w, true);
  return pos;
}

export default class Dialog extends React.Component {
  static defaultProps = {
    className: "",
    mask: true,
    visible: false,
    keyboard: true,
    closable: true,
    maskClosable: true,
    destroyOnClose: false,
    prefixCls: "rc-dialog",
    focusTriggerAfterClose: true,
    height: 400
  };

  inTransition = false;
  titleId = null;
  openTime = null;
  lastOutSideFocusNode = null;
  wrap = null;
  dialog = null;
  sentinelStart = null;
  sentinelEnd = null;
  dialogMouseDown = null;
  timeoutId = null;
  switchScrollingEffect = () => {};
  id = uuidv4();
  disX = 0;
  disY = 0;
  disW = 0;
  disH = 0;
  isDragResize = false;
  isDrag = false;
  mx = 0;
  my = 0;
  dx = 0;
  dy = 0;
  clientWidth =
    document.documentElement.clientWidth || document.body.clientWidth;
  clientHeight =
    document.documentElement.clientHeight || document.body.clientHeight;
  constructor(props) {
    super(props);
    this.titleId = `rcDialogTitle${uuid++}`;
    this.switchScrollingEffect = props.switchScrollingEffect || (() => {});
    this.state = {
      width: props.width,
      height: props.height,
      left: (this.clientWidth - props.width) / 2,
      top: (this.clientHeight - props.height) / 2
    };
    window.addEventListener("resize", () => {
      this.clientWidth =
        document.documentElement.clientWidth || document.body.clientWidth;
      this.clientHeight =
        document.documentElement.clientHeight || document.body.clientHeight;
      this.autoCenter()
    });
  }

  componentDidMount() {
    this.componentDidUpdate({});
    // if forceRender is true, set element style display to be none;
    if (
      (this.props.forceRender ||
        (this.props.getContainer === false && !this.props.visible)) &&
      this.wrap
    ) {
      this.wrap.style.display = "none";
    }
  }

  componentDidUpdate(prevProps) {
    const { visible, mask, focusTriggerAfterClose } = this.props;
    const mousePosition = this.props.mousePosition;
    if (visible) {
      // first show
      if (!prevProps.visible) {
        this.openTime = Date.now();
        this.switchScrollingEffect();
        this.tryFocus();
        const dialogNode = ReactDOM.findDOMNode(this.dialog);
        if (mousePosition) {
          const elOffset = offset(dialogNode);
          setTransformOrigin(
            dialogNode,
            `${mousePosition.x - elOffset.left}px ${mousePosition.y -
              elOffset.top}px`
          );
        } else {
          setTransformOrigin(dialogNode, "");
        }
      }
    } else if (prevProps.visible) {
      this.inTransition = true;
      if (mask && this.lastOutSideFocusNode && focusTriggerAfterClose) {
        try {
          this.lastOutSideFocusNode.focus();
        } catch (e) {
          this.lastOutSideFocusNode = null;
        }
        this.lastOutSideFocusNode = null;
      }
    }
  }

  componentWillUnmount() {
    const { visible, getOpenCount } = this.props;
    if ((visible || this.inTransition) && !getOpenCount()) {
      this.switchScrollingEffect();
    }
    clearTimeout(this.timeoutId);
  }

  tryFocus() {
    if (!contains(this.wrap, document.activeElement)) {
      this.lastOutSideFocusNode = document.activeElement;
      this.sentinelStart.focus();
    }
  }

  autoCenter = ()=>{
    this.setState({
      left: (this.clientWidth - this.state.width) / 2,
      top: (this.clientHeight - this.state.height) / 2
    });
  }

  onAnimateLeave = () => {
    const { afterClose } = this.props;
    // need demo?
    // https://github.com/react-component/dialog/pull/28
    if (this.wrap) {
      this.wrap.style.display = "none";
      this.autoCenter()
    }
    this.inTransition = false;
    this.switchScrollingEffect();
    if (afterClose) {
      afterClose();
    }
  };

  onDialogMouseDown = () => {
    this.dialogMouseDown = true;
  };

  onMaskMouseUp = () => {
    if (this.dialogMouseDown) {
      this.timeoutId = setTimeout(() => {
        this.dialogMouseDown = false;
      }, 0);
    }
  };

  onMaskClick = e => {
    // android trigger click on open (fastclick??)
    if (Date.now() - this.openTime < 300) {
      return;
    }
    if (e.target === e.currentTarget && !this.dialogMouseDown) {
      this.close(e);
    }
  };

  onKeyDown = e => {
    const props = this.props;
    if (props.keyboard && e.keyCode === KeyCode.ESC) {
      e.stopPropagation();
      this.close(e);
      return;
    }
    // keep focus inside dialog
    if (props.visible) {
      if (e.keyCode === KeyCode.TAB) {
        const activeElement = document.activeElement;
        const sentinelStart = this.sentinelStart;
        if (e.shiftKey) {
          if (activeElement === sentinelStart) {
            this.sentinelEnd.focus();
          }
        } else if (activeElement === this.sentinelEnd) {
          sentinelStart.focus();
        }
      }
    }
  };

  dragDown = event => {
    console.log(event);
    let e = event || window.event;
    this.mx = e.pageX;
    this.my = e.pageY;
    this.dx = this.state.left;
    this.dy = this.state.top;
    console.log(this.dx, this.dy, this.state.left, this.state.top);
    this.isDrag = true;
    document.onmousemove = event => {
      let e = event || window.event;
      let { dx, dy, mx, my, isDrag } = this;
      if (isDrag) {
        const MAX_X = this.clientWidth - this.state.width;
        const MAX_Y = this.clientHeight - this.state.height;
        let moveX = Math.min(
          Math.max(0, parseInt(dx + e.pageX - mx, 10)),
          MAX_X
        );
        let moveY = Math.min(
          Math.max(0, parseInt(dy + e.pageY - my, 10)),
          MAX_Y
        );
        console.log(moveY, moveX);
        this.setState({
          left: moveX,
          top: moveY
        });
      }
    };
  };
  dragUp = e => {
    this.isDrag = false;
  };
  resizeDown = event => {
    let e = event || window.event;
    this.disX = e.clientX;
    this.disY = e.clientY;
    this.disW = this.state.width;
    this.disH = this.state.height;
    this.isDragResize = true;
    document.onmousemove = event => {
      let e = event || window.event;
      let { disX, disY, disW, disH, isDragResize } = this;
      if (isDragResize) {
        console.log(e.clientX, disX, disW);
        this.setState({
          width: parseInt((e.clientX - disX) * MULTIPLE + disW, 10),
          height: parseInt(e.clientY - disY + disH, 10)
        });
      }
    };
  };

  resizeUp = e => {
    this.isDragResize = false;
  };

  getDialogElement = () => {
    const props = this.props;
    const closable = props.closable;
    const prefixCls = props.prefixCls;
    const dest = {};
    if (props.width !== undefined) {
      dest.width = props.width;
    }
    if (props.height !== undefined) {
      dest.height = props.height;
    }

    let footer;
    if (props.footer) {
      footer = (
        <div className={`${prefixCls}-footer`} ref={this.saveRef("footer")}>
          {props.footer}
        </div>
      );
    }

    let header;
    if (props.title) {
      header = (
        <div className={`${prefixCls}-header`} ref={this.saveRef("header")}>
          <div className={`${prefixCls}-title`} id={this.titleId}>
            {props.title}
          </div>
        </div>
      );
    }

    let closer;
    if (closable) {
      closer = (
        <button
          type="button"
          onClick={this.close}
          aria-label="Close"
          className={`${prefixCls}-close`}
        >
          {props.closeIcon || <span className={`${prefixCls}-close-x`} />}
        </button>
      );
    }

    const style = {
      ...props.style,
      ...dest,
      width: this.state.width,
      height: this.state.height,
      left: this.state.left,
      top: this.state.top
    };
    const sentinelStyle = { width: 0, height: 0, overflow: "hidden" };
    const transitionName = this.getTransitionName();
    const dialogElement = (
      <LazyRenderBox
        key="dialog-element"
        role="document"
        ref={this.saveRef("dialog")}
        style={style}
        className={`${prefixCls} ${props.className || ""}`}
        visible={props.visible}
        forceRender={props.forceRender}
        onMouseDown={this.onDialogMouseDown}
        id={this.id}
      >
        <div
          tabIndex={0}
          ref={this.saveRef("sentinelStart")}
          style={sentinelStyle}
          aria-hidden="true"
        />
        <div className={`${prefixCls}-content`}>
          {closer}
          {header}
          <div
            className={`${prefixCls}-body`}
            style={props.bodyStyle}
            ref={this.saveRef("body")}
            {...props.bodyProps}
          >
            {props.children}
          </div>
          {footer}
          <div
            className="ant-modal-move-header"
            onMouseDown={this.dragDown}
            onMouseUp={this.dragUp}
          ></div>
          <span
            style={{
              position: "absolute",
              right: 0,
              bottom: 0,
              cursor: "se-resize"
            }}
            onMouseDown={this.resizeDown}
            onMouseUp={this.resizeUp}
          >
            <RadiusBottomrightOutlined />
          </span>
        </div>
        <div
          tabIndex={0}
          ref={this.saveRef("sentinelEnd")}
          style={sentinelStyle}
          aria-hidden="true"
        />
      </LazyRenderBox>
    );

    return (
      <Animate
        key="dialog"
        showProp="visible"
        onLeave={this.onAnimateLeave}
        transitionName={transitionName}
        component=""
        transitionAppear
      >
        {props.visible || !props.destroyOnClose ? dialogElement : null}
      </Animate>
    );
  };

  getZIndexStyle = () => {
    const style = {};
    const props = this.props;
    if (props.zIndex !== undefined) {
      style.zIndex = props.zIndex;
    }
    return style;
  };

  getWrapStyle = () => {
    return { ...this.getZIndexStyle(), ...this.props.wrapStyle };
  };

  getMaskStyle = () => {
    return { ...this.getZIndexStyle(), ...this.props.maskStyle };
  };

  getMaskElement = () => {
    const props = this.props;
    let maskElement;
    if (props.mask) {
      const maskTransition = this.getMaskTransitionName();
      maskElement = (
        <LazyRenderBox
          style={this.getMaskStyle()}
          key="mask"
          className={`${props.prefixCls}-mask`}
          hiddenClassName={`${props.prefixCls}-mask-hidden`}
          visible={props.visible}
          {...props.maskProps}
        />
      );
      if (maskTransition) {
        maskElement = (
          <Animate
            key="mask"
            showProp="visible"
            transitionAppear
            component=""
            transitionName={maskTransition}
          >
            {maskElement}
          </Animate>
        );
      }
    }
    return maskElement;
  };

  getMaskTransitionName = () => {
    const props = this.props;
    let transitionName = props.maskTransitionName;
    const animation = props.maskAnimation;
    if (!transitionName && animation) {
      transitionName = `${props.prefixCls}-${animation}`;
    }

    return transitionName;
  };

  getTransitionName = () => {
    const props = this.props;
    let transitionName = props.transitionName;
    const animation = props.animation;
    if (!transitionName && animation) {
      transitionName = `${props.prefixCls}-${animation}`;
    }
    return transitionName;
  };

  close = e => {
    const { onClose } = this.props;
    if (onClose) {
      onClose(e);
    }
  };

  saveRef = name => node => {
    this[name] = node;
  };

  render() {
    const { props } = this;
    const { prefixCls, maskClosable } = props;
    const style = this.getWrapStyle();
    // clear hide display
    // and only set display after async anim, not here for hide
    if (props.visible) {
      style.display = null;
    }
    return (
      <div className={`${prefixCls}-root`}>
        {this.getMaskElement()}
        <div
          tabIndex={-1}
          onKeyDown={this.onKeyDown}
          className={`${prefixCls}-wrap ${props.wrapClassName || ""}`}
          ref={this.saveRef("wrap")}
          onClick={maskClosable ? this.onMaskClick : null}
          onMouseUp={maskClosable ? this.onMaskMouseUp : null}
          role="dialog"
          aria-labelledby={props.title ? this.titleId : null}
          style={style}
          {...props.wrapProps}
        >
          {this.getDialogElement()}
        </div>
      </div>
    );
  }
}
